
package com.insigma.business.qrcode.soldier._bff.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import com.insigma.business.qrcode.common.dto.CommonAdminAreaDTO;
import com.insigma.business.qrcode.common._consumer.CommonAdminAreaConsumer;
import com.insigma.business.qrcode.common.util.*;
import com.insigma.business.qrcode.common.vo.BaseResponse;
import com.insigma.business.qrcode.soldier.dto.BuriedPointItem;
import com.insigma.business.qrcode.soldier.dto.gov.BaseProviderImpl;
import com.insigma.business.qrcode.soldier.request.BuriedPointRequest;
import com.insigma.business.qrcode.soldier.request.BuriedPointResponse;
import com.insigma.business.qrcode.soldier.request.BuriedPointStatsRequest;
import com.insigma.business.qrcode.soldier._provider.BuriedPointProvider;
import com.insigma.business.qrcode.soldier._bff.CommonAreaBffProvider;
import com.insigma.business.qrcode.soldier.util.ThreadPoolManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;


@Slf4j
@Service
@Component("buriedPointProvider")
public class BuriedPointBffProviderImpl extends BaseProviderImpl implements BuriedPointProvider {

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private CommonAreaBffProvider commonAreaBffProvider;

    @Autowired
    private CommonAdminAreaConsumer commonAdminAreaConsumer;

    private String GLOBAL_PREFIX = "buriedPoint_";
    private String NAME_PREFIX = "buriedPoint_name_";
    private String ALL_PREFIX = "all";
    private String DAILY_PREFIX = "daily";
    private String SECTION_PREFIX = "section";

    private Long expiredTime = 60L;

    @Override
    public String setBuriedPointByWeek(BuriedPointRequest request) {

        if (request == null) {
            return null;
        }

        ThreadPoolManager.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    String weekNum = getWeekNum();
                    String key = GLOBAL_PREFIX + weekNum + "_" + request.getType() + "_" + request.getId();
                    String nameKey = NAME_PREFIX + weekNum + "_" + request.getType() + "_" + request.getId();

                    redisUtils.increment(key, 1L);
                    redisUtils.expire(key, TimeUnit.DAYS.toSeconds(expiredTime));
                    redisUtils.set(nameKey, request.getName(), TimeUnit.DAYS.toSeconds(expiredTime));
                } catch (Exception e) {
                    log.error("error:{}", e);
                }
            }
        });
        return "";
    }

    @Override
    public Integer getWeekCount(BuriedPointStatsRequest request) {
        BizCheckUtil.checkNotNull(request.getDate());
        BizCheckUtil.checkNotNull(request.getId());
        BizCheckUtil.checkNotNull(request.getType());
        String weekNum = String.valueOf(DateUtils.getWeekNum(request.getDate()));
        String key = GLOBAL_PREFIX + weekNum + "_" + request.getType() + "_" + request.getId();
        Object o = redisUtils.get(key);
        if (o == null) {
            return 0;
        }
        return Integer.valueOf((String) o);
    }

    @Override
    public BaseResponse<String> asyncSetSectionByWeek(BuriedPointRequest request) {


        ThreadPoolManager.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    String weekNum = getWeekNum();
                    String key = GLOBAL_PREFIX + "_" + weekNum + "_" + SECTION_PREFIX + request.getName();
                    String allKey = GLOBAL_PREFIX + "_" + ALL_PREFIX + "_" + weekNum + "_" + SECTION_PREFIX + request.getName();

                    redisUtils.increment(key, 1L);
                    redisUtils.increment(allKey, 1L);
                    redisUtils.expire(key, TimeUnit.DAYS.toSeconds(expiredTime));
                } catch (Exception e) {
                    log.error("按周-name统计埋点:{}", e);
                }
            }
        });
        return ResponseUtils.buildSuccessResponse("success");
    }

    @Override
    public String setBuriedPointOfSetByDay(BuriedPointRequest request) {

        if (request == null) {
            return null;
        }

        ThreadPoolManager.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                String id = request.getId();
                String format = DateFormatUtils.format(new Date(), "MM-dd");
                String key = GLOBAL_PREFIX + DAILY_PREFIX + "_" + request.getType() + "_" + format;
                redisUtils.sSet(key, id);
                redisUtils.expire(key, TimeUnit.DAYS.toSeconds(expiredTime));
            }
        });

        return "success";
    }

    @Override
    public String asyncSetBuriedPointOfHashByDayOfAdminCode(BuriedPointRequest request) {

        if (request == null) {
            return null;
        }

        ThreadPoolManager.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                String userId = request.getId();
                String adminAreaCode = commonAreaBffProvider.getCachedAdminCodeOfSoldier(userId);
                if (Strings.isBlank(adminAreaCode)) {
                    return;
                }

                String format = DateFormatUtils.format(new Date(), "MM-dd");
                String key = GLOBAL_PREFIX + DAILY_PREFIX + "HASH" + "_" + request.getType() + "_" + format;
                String setKey = GLOBAL_PREFIX + DAILY_PREFIX + "HASH_SET" + "_" + request.getType() + "_" + format;

                int level = AdminAreaCodeUtils.getLevel(adminAreaCode);
                for (int i = level; i > 2; i--) {
                    int levelLength = AdminAreaCodeUtils.getLevelLength(i);
                    String hashKey = adminAreaCode.substring(0, levelLength);

                    if (!request.isUnique() || !redisUtils.sHasKey(setKey, userId)) {
                        redisUtils.increment(key, hashKey, 1);
                    }
                }

                redisUtils.sSet(setKey, userId);
                redisUtils.expire(setKey, TimeUnit.DAYS.toSeconds(expiredTime));

                redisUtils.expire(key, TimeUnit.DAYS.toSeconds(expiredTime));
            }
        });

        return null;
    }

    @Override
    public String setCountPoint(BuriedPointRequest request) {

        if (request == null) {
            return null;
        }

        ThreadPoolManager.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    String all = "all";
                    String key = GLOBAL_PREFIX + all + "_" + request.getType() + "_" + request.getId();
                    String nameKey = NAME_PREFIX + all + "_" + request.getType() + "_" + request.getId();
                    redisUtils.increment(key, 1L);
                    redisUtils.set(nameKey, request.getName(), TimeUnit.DAYS.toSeconds(expiredTime));
                } catch (Exception e) {
                    log.error("error:{}", e);
                }
            }
        });

        return "success";
    }

    @Override
    public List<BuriedPointItem> getLastWeekSortedBuriedPointList(BuriedPointRequest request) {

        BizCheckUtil.checkNotNull(request, "参数不能为空");
        BizCheckUtil.checkNotBlank(request.getType(), "类型不能为空");

        Date date = DateUtils.addWeeks(new Date(), -1);
        int weekNum = DateUtils.getWeekNum(date);
        String lastWeekNum = String.valueOf(weekNum);
        List<BuriedPointItem> list = calcuPointCountAndSort(request.getType(), lastWeekNum);
        return list;
    }

    @Override
    public List<BuriedPointItem> getAllBuriedPointList(BuriedPointRequest request) {

        BizCheckUtil.checkNotNull(request, "参数不能为空");
        BizCheckUtil.checkNotBlank(request.getType(), "类型不能为空");
        List<BuriedPointItem> buriedPointItems = calcuPointCountAndSort(request.getType());
        return buriedPointItems;
    }

    @Override
    public Integer queryDailyVisitCount(Date date, String type) {
        String format = DateFormatUtils.format(date, "MM-dd");
        return queryDateVisitCount(format, type);
    }

    private Integer queryDateVisitCount(String dateFormat, String type) {
        String key = GLOBAL_PREFIX + DAILY_PREFIX + "_" + type + "_" + dateFormat;
        Long aLong = redisUtils.sCard(key);
        return aLong.intValue();
    }

    @Override
    public BaseResponse<BuriedPointResponse> statsBuriedPoint(BuriedPointStatsRequest request) {

        String weekNum = getWeekNum();


        LinkedHashMap<String, List> map = Maps.newLinkedHashMap();
        for (int i = Integer.valueOf(weekNum) - 8; i <= Integer.valueOf(weekNum); i++) {
            List<BuriedPointItem> list = calcuPointCountAndSort(request.getType(), String.valueOf(i));
            if (!CollectionUtils.isEmpty(list)) {
                map.put(String.valueOf(i), list);
            }
        }


        List<BuriedPointItem> list = calcuPointCountAndSort(request.getType(), ALL_PREFIX);
        map.put(ALL_PREFIX, list);

        BuriedPointResponse buriedPointResponse = new BuriedPointResponse();
        buriedPointResponse.setStats(map);
        return ResponseUtils.buildSuccessResponse(buriedPointResponse);
    }

    @Override
    public BaseResponse<BuriedPointResponse> statsTraffic(BuriedPointStatsRequest request) {


        int range = 7;

        LinkedHashMap<String, LinkedHashMap<String, Integer>> map = Maps.newLinkedHashMap();

        Date now = new Date();
        for (int i = range; i >= 0; i--) {
            List<BuriedPointItem> list = Lists.newArrayList();
            Date date = DateUtils.addDays(now, -i);
            String format = DateFormatUtils.format(date, "MM-dd");
            String keyReg = "traffic_" + format + "_" + "*";
            int allCount = 0;
            Set keys = redisUtils.keys(keyReg);
            if (!CollectionUtils.isEmpty(keys)) {
                for (Object key : keys) {
                    String keyStr = (String) key;
                    String api = cutIdFromKey(keyStr);
                    Integer count = Integer.valueOf((String) redisUtils.get(keyStr));
                    allCount += count;
                    list.add(new BuriedPointItem(api, null, count));
                }
            }
            list.add(new BuriedPointItem("all", null, allCount));
            Collections.sort(list, new Comparator<BuriedPointItem>() {
                @Override
                public int compare(BuriedPointItem o1, BuriedPointItem o2) {
                    return o2.getCount().compareTo(o1.getCount());
                }
            });
            if (!CollectionUtils.isEmpty(list)) {
                map.put(format, convertItemToLinkedMap(list));
            }
        }
        BuriedPointResponse buriedPointResponse = new BuriedPointResponse();
        buriedPointResponse.setStatsMap(map);
        return ResponseUtils.buildSuccessResponse(buriedPointResponse);
    }

    @Override
    public BaseResponse<List<BuriedPointItem>> statsSectionBuriedPoint(BuriedPointStatsRequest request) {


        Date date = request.getDate();
        if (date == null) {
            date = DateUtils.addWeeks(new Date(), -1);
        }

        String weekNum = String.valueOf(DateUtils.getWeekNum(date));
        String keyPrefix = GLOBAL_PREFIX + "_" + weekNum + "_" + SECTION_PREFIX;
        String keyReg = keyPrefix + "*";
        List<BuriedPointItem> list = Lists.newArrayList();

        Set keys = redisUtils.keys(keyReg);
        if (!CollectionUtils.isEmpty(keys)) {
            for (Object key : keys) {
                String keyStr = (String) key;
                String name = keyStr.replace(keyPrefix, "");
                Integer count = Integer.valueOf((String) redisUtils.get(keyStr));
                list.add(new BuriedPointItem(null, name, count));
            }
            Collections.sort(list, new Comparator<BuriedPointItem>() {
                @Override
                public int compare(BuriedPointItem o1, BuriedPointItem o2) {
                    return o2.getCount().compareTo(o1.getCount());
                }
            });
        }
        return ResponseUtils.buildSuccessResponse(list);
    }

    @Override
    public BaseResponse<BuriedPointResponse> statsDailyUsage(BuriedPointStatsRequest request) {

        int range = 14;

        LinkedHashMap<String, Integer> map = Maps.newLinkedHashMap();

        Date now = new Date();
        for (int i = range; i >= 0; i--) {
            Date date = DateUtils.addDays(now, -i);
            String format = DateFormatUtils.format(date, "MM-dd");
            String key = GLOBAL_PREFIX + DAILY_PREFIX + "_" + request.getType() + "_" + format;
            Long aLong = redisUtils.sCard(key);
            map.put(format, aLong.intValue());
        }
        BuriedPointResponse buriedPointResponse = new BuriedPointResponse();
        buriedPointResponse.setDailyStatsMap(map);
        return ResponseUtils.buildSuccessResponse(buriedPointResponse);
    }

    @Override
    public BuriedPointResponse statsHashDailyAdminUsage(BuriedPointStatsRequest request) {

        String adminAreaCode = request.getId();

        Date date = request.getDate();
        if (date == null) {
            date = new Date();
        }
        String format = DateFormatUtils.format(date, "MM-dd");
        String key = GLOBAL_PREFIX + DAILY_PREFIX + "HASH" + "_" + request.getType() + "_" + format;

        LinkedHashMap<String, Integer> result = new LinkedHashMap<>();
        List<CommonAdminAreaDTO> dto = commonAdminAreaConsumer.findSubAreaList(adminAreaCode).getDto();
        if (CollectionUtils.isEmpty(dto)) {
            if (AdminAreaCodeUtils.getLevel(adminAreaCode) == 5) {
                CommonAdminAreaDTO adminAreaById = commonAdminAreaConsumer.getAdminAreaById(adminAreaCode);
                String mapInt = (String) redisUtils.getMap(key, adminAreaCode);
                result.put(adminAreaById.getValue(), cleanInt(mapInt));
            }
        } else {
            for (CommonAdminAreaDTO commonAdminAreaDTO : dto) {
                String intStr = (String) redisUtils.getMap(key, AdminAreaCodeUtils.getCutdAreaCode(commonAdminAreaDTO.getId()));
                result.put(commonAdminAreaDTO.getValue(), cleanInt(intStr));
            }
        }

        BuriedPointResponse buriedPointResponse = new BuriedPointResponse();
        buriedPointResponse.setDailyStatsMap(result);
        return buriedPointResponse;
    }

    @Override
    public BuriedPointResponse statsSingleDailyAdminUsage(BuriedPointStatsRequest request) {

        String adminAreaCode = request.getId();

        Date date = request.getDate();
        if (date == null) {
            date = new Date();
        }
        String format = DateFormatUtils.format(date, "MM-dd");
        String key = GLOBAL_PREFIX + DAILY_PREFIX + "HASH" + "_" + request.getType() + "_" + format;
        String mapInt = (String) redisUtils.getMap(key, adminAreaCode);
        BuriedPointResponse buriedPointResponse = new BuriedPointResponse();
        buriedPointResponse.setDailyStats(cleanInt(mapInt));
        return buriedPointResponse;
    }

    private int cleanInt(String integer) {
        if (integer == null) {
            return 0;
        }
        return Integer.valueOf(integer);
    }

    private LinkedHashMap<String, Integer> convertItemToLinkedMap(List<BuriedPointItem> list) {
        LinkedHashMap<String, Integer> map = Maps.newLinkedHashMap();
        for (BuriedPointItem buriedPointItem : list) {
            map.put(buriedPointItem.getId(), buriedPointItem.getCount());
        }

        return map;
    }


    private List<BuriedPointItem> calcuPointCountAndSort(String type, String weekNum) {
        String keyReg = GLOBAL_PREFIX + weekNum + "_" + type + "*";
        List<BuriedPointItem> list = Lists.newArrayList();
        Set keys = redisUtils.keys(keyReg);
        if (!CollectionUtils.isEmpty(keys)) {
            for (Object key : keys) {
                String keyStr = (String) key;
                String id = cutIdFromKey(keyStr);
                String nameKey = NAME_PREFIX + weekNum + "_" + type + "_" + id;
                Integer count = Integer.valueOf((String) redisUtils.get(keyStr));
                String name = (String) redisUtils.get(nameKey);
                list.add(new BuriedPointItem(id, name, count));
            }
            Collections.sort(list, new Comparator<BuriedPointItem>() {
                @Override
                public int compare(BuriedPointItem o1, BuriedPointItem o2) {
                    return o2.getCount().compareTo(o1.getCount());
                }
            });
        }
        return list;
    }

    private List<BuriedPointItem> calcuPointCountAndSort(String type) {
        String all = "all";
        String keyReg = GLOBAL_PREFIX + all + "_" + type + "*";
        List<BuriedPointItem> list = Lists.newArrayList();
        Set keys = redisUtils.keys(keyReg);
        if (!CollectionUtils.isEmpty(keys)) {
            for (Object key : keys) {
                String keyStr = (String) key;
                String id = cutIdFromKey(keyStr);
                String nameKey = NAME_PREFIX + all + "_" + type + "_" + id;
                Integer count = Integer.valueOf((String) redisUtils.get(keyStr));
                String name = (String) redisUtils.get(nameKey);
                list.add(new BuriedPointItem(id, name, count));
            }
            Collections.sort(list, new Comparator<BuriedPointItem>() {
                @Override
                public int compare(BuriedPointItem o1, BuriedPointItem o2) {
                    return o2.getCount().compareTo(o1.getCount());
                }
            });
        }
        return list;
    }

    private String cutIdFromKey(String key) {
        int i = key.lastIndexOf("_");
        return key.substring(i + 1);
    }


    private String getWeekNum() {
        return String.valueOf(DateUtils.getWeekNum());
    }


}
